/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.systhreads;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CadmiumThread;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Context;
import fr.x9c.cadmium.kernel.Fail;
import fr.x9c.cadmium.kernel.FalseExit;
import fr.x9c.cadmium.kernel.Value;

/**
 * This class is the translation/implementation of 'caml_threadstatus' type. <br/>
 * Each instance contains a status information (alive or terminated) and
 * a lock / condition.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
public final class ThreadStatus {

    /** Thread status. */
    private boolean alive;

    /** Lock / condition. */
    private final Object lock;

    /** Thread. */
    private final CadmiumThread thread;

    /**
     * Creates an instance with status set to alive.
     * @param bc bytecode runner of thread
     * @param ct actual java thread, if <tt>null</tt> the thread is created
     */
    public ThreadStatus(final CodeRunner bc, final CadmiumThread ct) {
        this.lock = new Object();
        this.alive = true;
        this.thread = ct != null
            ? ct
            : new CadmiumThread(bc.getContext().getThreadGroup(), bc);
    } // end constructor(CodeRunner, CadmiumThread)

    /**
     * Encapsulates a thread status in a custom block.
     * @param ts thread status
     * @return thread status in a custom block
     */
    public static Value createValue(final ThreadStatus ts) {
        final Block b = Block.createCustom(CustomThreadStatus.SIZE,
                                           CustomThreadStatus.OPS);
        b.setCustom(ts);
        return Value.createFromBlock(b);
    } // end method 'createValue(ThreadStatus)'

    /**
     * Returns the underlying actual thread.
     * @return the underlying actual thread
     */
    public CadmiumThread getThread() {
        return this.thread;
    } // end method 'getThread()'

    /**
     * Sets the status to terminated, notifies waiting threads,
     * and stops the underlying thread.
     */
    public void terminate() {
        synchronized (this.lock) {
            this.alive = false;
            this.lock.notifyAll();
            this.thread.asksToStop();
            //this.thread.stop();
        } // end synchronized
    } // end method 'terminate()'

    /**
     * Joins the thread, that is waits for its termination.
     * @param ctxt context - should not be <tt>null</tt>
     * @throws FalseExit if another threads has exited the program
     * @throws Fail.Exception if an asynchronous exception should be thrown
     */
    public void join(final Context ctxt) throws FalseExit, Fail.Exception {
        try {
            this.thread.join();
        } catch (final InterruptedException ie) {
            final FalseExit fe = FalseExit.createFromContext(ctxt);
            fe.fillInStackTrace();
            throw fe;
        } // end try/catch
    } // end method 'join()'

    /**
     * Suspends the thread.
     */
    public void suspend() {
        this.thread.pause();
    } // end method 'suspend()'

    /**
     * Resumes the thread.
     */
    public void resume() {
        this.thread.wakeUp();
    } // end method 'resume()'

    /**
     * Tests whether the thread is suspended.
     * @return <tt>true</tt> if the thread is suspended,
     *         <tt>false</tt> otherwise
     */
    public boolean isSuspended() {
        return this.thread.isPaused();
    } // end method 'isSuspended()'

    /**
     * Tests whether the thread is alive.
     * @return <tt>true</tt> if the thread is alive, <tt>false</tt> otherwise
     */
    public boolean isAlive() {
        return this.alive;
    } // end method 'isAlive()'

    /**
     * Waits until the status is set to terminated.
     * @param ctxt context - should not be <tt>null</tt>
     */
    void waitTerm(final Context ctxt) throws FalseExit, Fail.Exception {
        while (this.alive) {
            synchronized (this.lock) {
                try {
                    this.lock.wait();
                } catch (final InterruptedException ie) {
                    final FalseExit fe = FalseExit.createFromContext(ctxt);
                    fe.fillInStackTrace();
                    throw fe;
                } // end try/catch
            } // end synchronized
        } // end while
    } // end method 'waitTerm()'

} // end class 'ThreadStatus'
